﻿// <copyright file="List.cs" company="EDD Solutions Ltd.">
// Copyright (c) EDD Solutions Ltd. 2010.
//
// This file is part of Sharepoint.Manipulator.Net.
//
// Sharepoint.Manipulator.Net is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Sharepoint.Manipulator.Net is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Sharepoint.Manipulator.Net.  If not, see http://www.gnu.org/licenses/.
// </copyright>
// <author>$Author$</author>
// <date>$Date$</date>
// <revision>$Revision$</revision>
namespace Sharepoint.Manipulator.Net
{
    using System;
    using System.Collections.Generic;
    using System.Net;
    using System.Text;
    using System.Xml;
    using Sharepoint.Manipulator.Net.uk.co.eddsolutions.sharepoint.lists;

    /// <summary>
    /// Implementation of <code>Enitity</code> used to access list data.
    /// </summary>
    public class List : AbstractEntity, IExists, IUpdate
    {
        /// <summary>
        /// The end of the URL to access Lists Web Services.
        /// </summary>
        public const string URLLISTSUFFIX = @"/_vti_bin/lists.asmx";

        /// <summary>
        /// The service to use to comminucate with the Sharepoint List.
        /// </summary>
        private Lists listService;

        /// <summary>
        /// The namespace manager for Sharepoint results xml
        /// </summary>
        private XmlNamespaceManager namespaceManager;

        /// <summary>
        /// Initializes a new instance of the List class.
        /// Uses the default NetworkCredentials.
        /// </summary>
        /// <param name="sharepointSiteUrl">the root URL of the sharepoint site to connect to.</param>
        /// <param name="listName">the name of the list to access data from.</param>
        public List(string sharepointSiteUrl, string listName)
            : this(sharepointSiteUrl, listName, System.Net.CredentialCache.DefaultCredentials)
        {
        }

        /// <summary>
        /// Initializes a new instance of the List class.
        /// Uses the default NetworkCredentials.
        /// </summary>
        /// <param name="sharepointSiteUrl">the root URL of the sharepoint site to connect to.</param>
        /// <param name="listName">the name of the list to access data from.</param>
        /// <param name="credentials">the credentials to use to access the sharepoint site</param>
        public List(string sharepointSiteUrl, string listName, ICredentials credentials)
            : base(sharepointSiteUrl, listName)
        {
            this.listService = new Lists();
            this.listService.Url = sharepointSiteUrl + URLLISTSUFFIX;
            this.listService.Credentials = this.Credentials;
            XmlNameTable nameTable = new NameTable();
            this.namespaceManager = new XmlNamespaceManager(nameTable);
            this.namespaceManager.AddNamespace("soap", "http://schemas.microsoft.com/sharepoint/soap/");
            this.namespaceManager.AddNamespace("rs", "urn:schemas-microsoft-com:rowset");
            this.namespaceManager.AddNamespace("z", "#RowsetSchema");
        }

        /// <summary>
        /// Initializes a new instance of the List class.
        /// Uses the default NetworkCredentials.
        /// </summary>
        /// <param name="sharepointSiteUrl">the root URL of the sharepoint site to connect to.</param>
        /// <param name="listName">the name of the list to access data from.</param>
        /// <param name="username">the username to use to access the sharepoint site</param>
        /// <param name="password">the password to use to access the sharepoint site</param>
        /// <param name="domain">the domain to use to access the sharepoint site</param>
        public List(string sharepointSiteUrl, string listName, string username, string password, string domain)
            : this(sharepointSiteUrl, listName, new NetworkCredential(username, password, domain))
        {
        }

        /// <summary>
        /// Initializes a new instance of the List class.
        /// Uses the default NetworkCredentials.
        /// </summary>
        /// <param name="sharepointSiteUrl">the root URL of the sharepoint site to connect to.</param>
        /// <param name="listName">the name of the list to access data from.</param>
        /// <param name="username">the username to use to access the sharepoint site</param>
        /// <param name="password">the password to use to access the sharepoint site</param>
        public List(string sharepointSiteUrl, string listName, string username, string password)
            : this(sharepointSiteUrl, listName, new NetworkCredential(username, password))
        {
        }

        /// <summary>
        /// Exists to see if the list exists
        /// </summary>
        /// <returns>treu if the named list exist false otherwise</returns>
        public bool ListExists()
        {
            XmlNode listNode = this.listService.GetList(this.EntityID);

            if (listNode == null)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// Exists to see if the list item exists
        /// </summary>
        /// <param name="itemId">the unique id of the item.</param>
        /// <param name="viewFields">the list of field values to return</param>
        /// <returns>a Dictionary of fieldName=Value pairs</returns>
        public IDictionary<string, string> GetListItem(string itemId, IList<string> viewFields)
        {
            string whereClause = "<Where>"
                + "<Eq><FieldRef Name='ID' /><Value Type='Text'>" + itemId + "</Value></Eq>"
                + "</Where>";
            XmlNode resultNode = this.Query(whereClause, viewFields);

            //// check results
            XmlNode dataElement = resultNode.SelectSingleNode("rs:data/z:row", this.namespaceManager);
            IDictionary<string, string> results = new Dictionary<string, string>();
            foreach (string viewFieldName in viewFields)
            {
                XmlAttribute attribute = dataElement.Attributes["ows_" + viewFieldName];
                if (attribute != null)
                {
                    results.Add(viewFieldName, attribute.Value);
                }
            }

            return results;
        }

        /// <summary>
        /// Update a List Item
        /// </summary>
        /// <param name="itemId">the unique id of the list item</param>
        /// <param name="updateFieldValues">the fieldname=value pairs to update</param>
        /// <returns>true if the update succeeded, false otherwise</returns>
        public bool UpdateItem(string itemId, IDictionary<string, string> updateFieldValues)
        {
            /*Create an XmlDocument object and construct a Batch element and its attributes. Note that an empty ViewName parameter causes the method to use the default view. */
            System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
            System.Xml.XmlElement batchElement = doc.CreateElement("Batch");
            batchElement.SetAttribute("OnError", "Continue");
            batchElement.SetAttribute("ListVersion", "1");
            ////batchElement.SetAttribute("ViewName", strViewID);

            StringBuilder updateFieldsBuffer = new StringBuilder();
            updateFieldsBuffer.Append("<Field Name='ID' >");
            updateFieldsBuffer.Append(itemId);
            updateFieldsBuffer.Append("</Field>");
            foreach (string updateFieldName in updateFieldValues.Keys)
            {
                updateFieldsBuffer.Append("<Field Name='");
                updateFieldsBuffer.Append(updateFieldName);
                updateFieldsBuffer.Append("' >");
                updateFieldsBuffer.Append(updateFieldValues[updateFieldName]);
                updateFieldsBuffer.Append("</Field>");
            }

            /*Specify methods for the batch post using CAML. To update or delete, specify the ID of the item, and to update or add, specify 
            the value to place in the specified column.*/
            batchElement.InnerXml = "<Method ID='1' Cmd='Update'>" +
               updateFieldsBuffer.ToString() +
               "</Method>";

            /*Update list items. This example uses the list GUID, which is recommended, but the list display name will also work.*/
            XmlNode resultNode;
            resultNode = this.listService.UpdateListItems(this.EntityID, batchElement);

            // check results
            XmlNode errorElement = resultNode.SelectSingleNode("soap:Result/soap:ErrorCode", this.namespaceManager);
            string errorValue = errorElement.InnerText.Substring(2);
            int error;
            if (int.TryParse(errorValue, out error))
            {
                if (error == 0)
                {
                    return true;
                }

                return false;
            }
            else
            {
                return false;
            }
        }

        #region IExists Members

        /// <summary>
        /// Check to see if an item exists.
        /// </summary>
        /// <param name="itemId">the unique id of the item.</param>
        /// <returns>true if the item exists false if not.</returns>
        public bool ItemExists(string itemId)
        {
            string whereClause = "<Where>"
                + "<Eq><FieldRef Name='ID' /><Value Type='Text'>" + itemId + "</Value></Eq>"
                + "</Where>";
            XmlNode resultNode = this.Query(whereClause, new List<string>());

            // check results
            XmlNode dataElement = resultNode.SelectSingleNode("rs:data", this.namespaceManager);
            XmlAttribute itemCountAttribute = dataElement.Attributes["ItemCount"];
            string itemCountValue = itemCountAttribute.Value;
            if (itemCountValue == "0")
            {
                return false;
            }

            return true;
        }

        #endregion

        /// <summary>
        /// Query Sharepoint
        /// </summary>
        /// <param name="whereClause">the search clause</param>
        /// <param name="viewFields">the list of fields to return</param>
        /// <returns>the xml results</returns>
        private XmlNode Query(string whereClause, IList<string> viewFields)
        {
            XmlDocument camlDocument = new XmlDocument();

            // Create where clause
            XmlNode queryNode = camlDocument.CreateElement("Query");
            queryNode.InnerXml = whereClause;

            // Create list of fields to return
            XmlNode viewFieldsNode = camlDocument.CreateElement("ViewFields");
            StringBuilder viewFieldsBuffer = new StringBuilder();
            foreach (string viewFieldName in viewFields)
            {
                viewFieldsBuffer.Append("<FieldRef Name='");
                viewFieldsBuffer.Append(viewFieldName);
                viewFieldsBuffer.Append("' />");
            }

            viewFieldsNode.InnerXml = viewFieldsBuffer.ToString();

            // Create options 
            XmlNode queryOptionsNode = camlDocument.CreateElement("QueryOptions");

            // submit query
            XmlNode resultNode = this.listService.GetListItems(this.EntityID, null, queryNode, viewFieldsNode, null, queryOptionsNode, null);
            return resultNode;
        }
    }
}
